/**
 * DSM - Distributed Search Manager
 * Developed by Milspec Research International Pty Ltd
 * $Author: skahl $
 * $Revision: 1.19 $
 * $Date: 2007/12/05 22:28:06 $
 * (c)Copyright 2004
 * education.au limited
 * DEST
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * Neither the names education.au limited, DEST nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 * To the extent permitted by law, the copyright owners of this software and
 * its contributors:
 * (i) exclude all warranties in relation to the software; and
 * (ii) exclude liability for all losses, costs, expenses and damages arising
 * in any way from the use of the software whether arising from or in
 * relation to breach of contract, negligence or any other tort, in equity
 * or otherwise. If the software is in breach of a warranty which is
 * implied by law, the copyright owners and contributors liability is
 * limited to the replacement of the software.
 *
 * @author	gsingh
 */

package au.edu.educationau.opensource.dsm.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;

import org.apache.log4j.Logger;
import org.xml.sax.helpers.AttributesImpl;

import au.edu.educationau.opensource.dsm.DeusExMachina;
import au.edu.educationau.opensource.dsm.adapters.SearchAdapterProperties;
import au.edu.educationau.opensource.dsm.obj.ResultArtifact;
import au.edu.educationau.opensource.dsm.obj.ResultCategory;
import au.edu.educationau.opensource.dsm.obj.ResultMetadata;
import au.edu.educationau.opensource.dsm.obj.SearchCriteria;
import au.edu.educationau.opensource.dsm.obj.SearchResult;
import au.edu.educationau.opensource.dsm.obj.SearchResultSet;
import au.edu.educationau.opensource.dsm.service.PropertiesService;
import au.edu.educationau.opensource.dsm.util.EducationAuUtils;
import au.edu.educationau.opensource.dsm.util.StringUtils;
import au.edu.educationau.opensource.dsm.worker.SearchAdapterFactory;
import au.edu.educationau.opensource.dsm.worker.SearchJob;
import au.edu.educationau.opensource.dsm.worker.SearchResultSorter;

/**
 * DSM API servlet - Used by API.edna and all clients
 * 
 */

public class DSMAPIXml extends HttpServlet {
	static Logger logger = Logger.getLogger(DSMAPIXml.class.getName());

	private static final String COPYRIGHT = "1996-2010 by Commonwealth of Australia. Permission is granted to copy and distribute this document in whole or in part, provided it is done for educational and non-commercial purposes only and that this entire copyright notice remains attached.";
	private static final String SOURCE = "This information is provided by the EdNA Online project, a joint initiative of the state, territory and Australian governments.";
	private static final String MODE = "simple";

	/** Servlet Context */
	protected ServletContext servletContext;

	/**
	 * Servlet init
	 * 
	 * @param config
	 * @exception ServletException
	 */
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		this.servletContext = config.getServletContext();
	}

	/**
	 * Service handles both POST and GET requests. Builds XML output result set.
	 * 
	 * @param request
	 * @param response
	 * @exception ServletException
	 * @exception IOException
	 */
	public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		response.setContentType("text/xml; charset=UTF-8");

		boolean isCompletedSet = true;
		String allowDuplicates = StringUtils.checkString(request.getParameter("dupes"), "true");
		int start = EducationAuUtils.getInteger(request.getParameter("start"), 1, 200);
		int pageSize = EducationAuUtils.getInteger(request.getParameter("ps"), 10, 200);
		long token = EducationAuUtils.getLong(request.getParameter("token"), DeusExMachina.generateToken());
		SearchCriteria criteria;
		SearchResult[] results = new SearchResult[0];
		String count = "0";
		String found = "0";
		String total = "0";
		String theoreticalTotal = "0";
		String rset = StringUtils.checkString(request.getParameter("rset"), ""); // result
		// set
		// by
		// source
		String timeTaken = "0.00";

		// check if the job exists
		if (DeusExMachina._SearchManagerService().jobExists(token)) {
			SearchJob job = DeusExMachina._SearchManagerService().getJob(token);
			criteria = job.getSearchCriteria();
			DeusExMachina._SearchManagerService().monitorJob(token);
		} else {
			// Build criteria
			criteria = DeusExMachina._SearchManagerService().buildSearchCriteria(request, token);
			// generate a new search
			DeusExMachina._SearchManagerService().search(criteria);
		}
		DeusExMachina.logSearchToDatabase(DeusExMachina._SearchManagerService().getJob(token), request);

		/** Priority Sorting - Added 25/5/04 by gsingh */
		String[] sources = criteria.getSources();
		String[] prioritisedSources = new String[sources.length];
		String priority;
		String sortAlgorithm = StringUtils.checkString(request.getParameter("sort"), SearchResultSorter.SORTED_BY_PRIORITY_INTERLEAVED);

		for (int i = 0; i < sources.length; i++) {
			priority = StringUtils.checkString(request.getParameter("priority." + sources[i]), "");
			if (priority.length() <= 0) {
				if (SearchAdapterFactory.adapterExists(sources[i])) {
					priority = "" + SearchAdapterFactory.getAdapterProperties(sources[i]).getPriority();
				} else {
					priority = "1000";
				}
			}
			prioritisedSources[i] = priority + "." + sources[i];
		}

		Arrays.sort(prioritisedSources);

		SearchResultSet set = DeusExMachina._SearchResultService().getResults(token);

		if (null != set) {
			isCompletedSet = set.isCompletedSet();
			total = "" + set.getRawResultCount();
			if (rset.length() <= 0) {
				theoreticalTotal = "" + set.getTotalTheoreticalResultsAvailable();
			} else {
				theoreticalTotal = "" + set.getTheoreticalResultsAvailable(rset);
			}
			if (rset.equals("")) {
				results = set.getDistributedResults(start, pageSize, criteria.getMaxResults(), sortAlgorithm, allowDuplicates.equals("true"), prioritisedSources);
				timeTaken = "" + set.getTotalTimeTaken() / 1000f;
			} else {
				results = set.getResultsbySource(start, pageSize, rset, sortAlgorithm);
				timeTaken = "" + set.getTimeTakenBySource(rset) / 1000f;
			}
			found = "" + set.getSetResultCount();
			if (null != results) {
				count = "" + results.length;
			}
		}

		// Build output

		logger.info("RESPONSE " + token + " " + found + " " + timeTaken + " 200");

		PrintWriter out = response.getWriter();
		try {
			StreamResult streamResult = new StreamResult(out);
			SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
			// SAX2.0 ContentHandler.
			TransformerHandler hd = tf.newTransformerHandler();
			Transformer serializer = hd.getTransformer();
			serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			serializer.setOutputProperty(OutputKeys.INDENT, "yes");
			hd.setResult(streamResult);
			hd.startDocument();
			// START <searchresults>
			AttributesImpl atts = new AttributesImpl();
			hd.startElement("", "", "searchresults", atts);

			// START <summary>
			hd.startElement("", "", "summary", atts);

			// Monitor Values
			hd.startElement("", "", "copyright", atts);
			hd.characters(COPYRIGHT.toCharArray(), 0, COPYRIGHT.length());
			hd.endElement("", "", "copyright");

			hd.startElement("", "", "source", atts);
			hd.characters(SOURCE.toCharArray(), 0, SOURCE.length());
			hd.endElement("", "", "source");

			hd.startElement("", "", "mode", atts);
			hd.characters(MODE.toCharArray(), 0, MODE.length());
			hd.endElement("", "", "mode");

			hd.startElement("", "", "token", atts);
			hd.characters(("" + token).toCharArray(), 0, ("" + token).length());
			hd.endElement("", "", "token");

			hd.startElement("", "", "count", atts);
			hd.characters(count.toCharArray(), 0, count.length());
			hd.endElement("", "", "count");

			hd.startElement("", "", "found", atts);
			hd.characters(found.toCharArray(), 0, found.length());
			hd.endElement("", "", "found");

			hd.startElement("", "", "theoreticaltotal", atts);
			hd.characters(theoreticalTotal.toCharArray(), 0, theoreticalTotal.length());
			hd.endElement("", "", "theoreticaltotal");

			hd.startElement("", "", "total", atts);
			hd.characters(total.toCharArray(), 0, total.length());
			hd.endElement("", "", "total");

			atts.addAttribute("", "", "units", "", "seconds");
			hd.startElement("", "", "timetaken", atts);
			atts.clear();
			hd.characters(timeTaken.toCharArray(), 0, timeTaken.length());
			hd.endElement("", "", "timetaken");

			hd.startElement("", "", "completed", atts);
			hd.characters(("" + isCompletedSet).toCharArray(), 0, ("" + isCompletedSet).length());
			hd.endElement("", "", "completed");

			// Query Parameters
			atts.addAttribute("", "", "name", "", "q");
			atts.addAttribute("", "", "desc", "", "Query Text");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(criteria.getQuery().toCharArray(), 0, criteria.getQuery().length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "start");
			atts.addAttribute("", "", "desc", "", "Start Result Number");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + start).toCharArray(), 0, ("" + start).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "ps");
			atts.addAttribute("", "", "desc", "", "Page Size");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + pageSize).toCharArray(), 0, ("" + pageSize).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "mr");
			atts.addAttribute("", "", "desc", "", "Maximum Results");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + criteria.getMaxResults()).toCharArray(), 0, ("" + criteria.getMaxResults()).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "cs");
			atts.addAttribute("", "", "desc", "", "Case Sensitive");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + criteria.isCaseSensitive()).toCharArray(), 0, ("" + criteria.isCaseSensitive()).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "kc");
			atts.addAttribute("", "", "desc", "", "Keyword Constraint");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + criteria.getKeywordConstraint()).toCharArray(), 0, ("" + criteria.getKeywordConstraint()).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "rset");
			atts.addAttribute("", "", "desc", "", "Repository Filter");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(rset.toCharArray(), 0, rset.length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "ss");
			atts.addAttribute("", "", "desc", "", "Search Strategy");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(("" + criteria.getSearchStrategy()).toCharArray(), 0, ("" + criteria.getSearchStrategy()).length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "sort");
			atts.addAttribute("", "", "desc", "", "Sort Algorithm");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(sortAlgorithm.toCharArray(), 0, sortAlgorithm.length());
			hd.endElement("", "", "qp");

			atts.addAttribute("", "", "name", "", "dupes");
			atts.addAttribute("", "", "desc", "", "Allow Duplicate Results");
			hd.startElement("", "", "qp", atts);
			atts.clear();
			hd.characters(allowDuplicates.toCharArray(), 0, allowDuplicates.length());
			hd.endElement("", "", "qp");

			for (int i = 0; i < criteria.getSources().length; i++) {
				atts.addAttribute("", "", "name", "", "sr");
				atts.addAttribute("", "", "desc", "", "Source Resource");
				hd.startElement("", "", "qp", atts);
				atts.clear();
				hd.characters(criteria.getSources()[i].toCharArray(), 0, criteria.getSources()[i].length());
				hd.endElement("", "", "qp");
			}

			for (int i = 0; i < criteria.getThesauri().length; i++) {
				atts.addAttribute("", "", "name", "", "th");
				atts.addAttribute("", "", "desc", "", "Thesaurus");
				hd.startElement("", "", "qp", atts);
				atts.clear();
				hd.characters(criteria.getThesauri()[i].toCharArray(), 0, criteria.getThesauri()[i].length());
				hd.endElement("", "", "qp");
			}

			// custom parameters
			if (criteria.hasCustomParams()) {
				for (Enumeration e = criteria.getCustomParams(); e.hasMoreElements();) {
					String customParam = (String) e.nextElement();
					String[] customParamValues = criteria.getCustomParamValues(customParam);
					for (int i = 0; i < customParamValues.length; i++) {
						atts.addAttribute("", "", "name", "", customParam);
						atts.addAttribute("", "", "desc", "", "Adapter Parameter");
						hd.startElement("", "", "qp", atts);
						atts.clear();
						hd.characters(customParamValues[i].toCharArray(), 0, customParamValues[i].length());
						hd.endElement("", "", "qp");
					}
				}
			}

			// END </searchresults>
			hd.endElement("", "", "summary");

			// SOURCES <sources>

			hd.startElement("", "", "sources", atts);
			SearchAdapterProperties properties;
			for (int i = 0; i < criteria.getSources().length; i++) {
				properties = SearchAdapterFactory.getAdapterProperties(criteria.getSources()[i]);
				if (null != properties) {
					atts.addAttribute("", "", "id", "", properties.getAdapterCode());
					hd.startElement("", "", "source", atts);
					atts.clear();
					Map queryCounts = set.getFacetQueryCounts(criteria.getSources()[i]);
					// facet query count
					if (queryCounts != null && queryCounts.keySet() != null) {
						for (Iterator it = queryCounts.keySet().iterator(); it.hasNext();) {
							String query = (String) it.next();
							int querycount = ((Integer) queryCounts.get(query)).intValue();
							atts.addAttribute("", "", "query", "", query);
							atts.addAttribute("", "", "count", "", String.valueOf(querycount));
							hd.startElement("", "", "facet", atts);
							atts.clear();
							hd.endElement("", "", "facet");
						}
					}
					hd.startElement("", "", "title", atts);
					hd.characters(properties.getDisplayTitle().toCharArray(), 0, properties.getDisplayTitle().length());
					hd.endElement("", "", "title");
					hd.startElement("", "", "description", atts);
					hd.characters(properties.getDescription().toCharArray(), 0, properties.getDescription().length());
					hd.endElement("", "", "description");
					hd.startElement("", "", "link", atts);
					hd.characters(properties.getWebsiteURL().toCharArray(), 0, properties.getWebsiteURL().length());
					hd.endElement("", "", "link");
					hd.startElement("", "", "image", atts);
					hd.characters(properties.getImageURL().toCharArray(), 0, properties.getImageURL().length());
					hd.endElement("", "", "image");

					if (null != set) {
						// numresults
						String numResults = "" + set.getResultCountbySource(properties.getAdapterCode());
						hd.startElement("", "", "numresults", atts);
						hd.characters(numResults.toCharArray(), 0, numResults.length());
						hd.endElement("", "", "numresults");

						// responsetime
						String responseTime = "" + (set.getTimeTakenBySource(properties.getAdapterCode()) / 1000f);
						atts.addAttribute("", "", "units", "", "seconds");
						hd.startElement("", "", "responsetime", atts);
						atts.clear();
						hd.characters(responseTime.toCharArray(), 0, responseTime.length());
						hd.endElement("", "", "responsetime");

						// response code
						String responseCode = set.getResponseCodeOfSource(properties.getAdapterCode());
						String responseDesc = PropertiesService.getStringProperty("responsecode.message." + responseCode, "");
						atts.addAttribute("", "", "desc", "", responseDesc);
						hd.startElement("", "", "responsecode", atts);
						atts.clear();
						hd.characters(responseCode.toCharArray(), 0, responseCode.length());
						hd.endElement("", "", "responsecode");

						// diagnostic
						String diagnostic = set.getDiagnosticOfSource(properties.getAdapterCode());
						hd.startElement("", "", "diagnostic", atts);
						hd.characters(diagnostic.toCharArray(), 0, diagnostic.length());
						hd.endElement("", "", "diagnostic");
					}
					hd.endElement("", "", "source");

				}
			}
			hd.endElement("", "", "sources");

			// Result Items
			if (null != results && results.length > 0) {
				SearchResult result;
				ResultCategory[] categories;
				ResultMetadata[] metadata;
				ResultCategory category;
				ResultMetadata metadataEntry;

				for (int i = 0; i < results.length; i++) {
					result = results[i];
					if (null != result) {
						String categoryURI = SearchAdapterFactory.getCategoryURI(result.getSource());
						atts.addAttribute("", "", "source", "", result.getSource());
						hd.startElement("", "", "item", atts);
						atts.clear();
						// Basic elements
						hd.startElement("", "", "title", atts);
						hd.characters(result.getTitle().toCharArray(), 0, result.getTitle().length());
						hd.endElement("", "", "title");
						hd.startElement("", "", "link", atts);
						if (result.getLink() != null) {
							hd.characters(result.getLink().toCharArray(), 0, result.getLink().length());
						}
						hd.endElement("", "", "link");
						hd.startElement("", "", "relevance", atts);
						hd.characters(("" + result.getRelevance()).toCharArray(), 0, ("" + result.getRelevance()).length());
						hd.endElement("", "", "relevance");
						hd.startElement("", "", "description", atts);
						hd.characters(result.getDescription().toCharArray(), 0, result.getDescription().length());
						hd.endElement("", "", "description");

						hd.startElement("", "", "download", atts);
						hd.characters(result.getDownload().toCharArray(), 0, result.getDownload().length());
						hd.endElement("", "", "download");

						hd.startElement("", "", "preview", atts);
						hd.characters(result.getPreview().toCharArray(), 0, result.getPreview().length());
						hd.endElement("", "", "preview");

						hd.startElement("", "", "identifier", atts);
						hd.characters(result.getIdentifier().toCharArray(), 0, result.getIdentifier().length());
						hd.endElement("", "", "identifier");

						hd.startElement("", "", "rights", atts);
						hd.characters(result.getRights().toCharArray(), 0, result.getRights().length());
						hd.endElement("", "", "rights");

						hd.startElement("", "", "metadatascheme", atts);
						hd.characters(result.getMetadataScheme().toCharArray(), 0, result.getMetadataScheme().length());
						hd.endElement("", "", "metadatascheme");

						hd.startElement("", "", "contentpackage", atts);
						hd.characters(result.getContentPackage().toCharArray(), 0, result.getContentPackage().length());
						hd.endElement("", "", "contentpackage");

						if (result.getDate() != null && result.getDate().length() > 0) {
							hd.startElement("", "", "date", atts);
							hd.characters(result.getDate().toCharArray(), 0, result.getDate().length());
							hd.endElement("", "", "date");
						}

						// Category Data
						categories = result.getResultCategories();
						if (null != categories) {
							hd.startElement("", "", "categories", atts);
							for (int j = 0; j < categories.length; j++) {
								category = categories[j];
								if (null != category) {
									hd.startElement("", "", "category", atts);
									hd.startElement("", "", "title", atts);
									hd.characters(category.getName().toCharArray(), 0, category.getName().length());
									hd.endElement("", "", "title");
									for (Iterator it = category.getPaths().iterator(); it.hasNext();) {
										hd.startElement("", "", "link", atts);
										String path = it.next().toString();
										hd.characters((categoryURI + path).toCharArray(), 0, (categoryURI + path).length());
										hd.endElement("", "", "link");
									}
									String alias = category.getAlias().trim();
									if (alias.length() > 0) {
										hd.startElement("", "", "alias", atts);
										hd.characters(alias.toCharArray(), 0, alias.length());
										hd.endElement("", "", "alias");
									}
									hd.endElement("", "", "category");
								} // if
							} // for
							hd.endElement("", "", "categories");
						} // if

						// metadata
						metadata = result.getResultMetadata();
						if (null != metadata) {
							for (int j = 0; j < metadata.length; j++) {
								metadataEntry = metadata[j];
								if (null != metadataEntry) {
									atts.addAttribute("", "", "name", "", metadataEntry.getName());
									hd.startElement("", "", "metadata", atts);
									atts.clear();
									hd.characters(metadataEntry.getValue().toCharArray(), 0, metadataEntry.getValue().length());
									hd.endElement("", "", "metadata");
								} // if
							} // for
						} // if
						
						// artifacts
						ResultArtifact[] artifacts = result.getArtifacts();
						if (null != artifacts) {
							hd.startElement("", "", "artifacts", atts);
							for (int k = 0; k < artifacts.length; k++) {
								ResultArtifact artifact = artifacts[k];
								if (null != artifact) {
									hd.startElement("", "", "artifact", atts);
									hd.startElement("", "", "id", atts);
									hd.characters(artifact.getId().toCharArray(), 0, artifact.getId().length());
									hd.endElement("", "", "id");
									hd.startElement("", "", "name", atts);
									hd.characters(artifact.getName().toCharArray(), 0, artifact.getName().length());
									hd.endElement("", "", "name");
									hd.startElement("", "", "description", atts);
									hd.characters(artifact.getDescription().toCharArray(), 0, artifact.getDescription().length());
									hd.endElement("", "", "description");
									hd.startElement("", "", "size", atts);
									hd.characters(String.valueOf(artifact.getSize()).toCharArray(), 0, String.valueOf(artifact.getSize()).length());
									hd.endElement("", "", "size");
									hd.startElement("", "", "mimetype", atts);
									hd.characters(artifact.getMimeType().toCharArray(), 0, artifact.getMimeType().length());
									hd.endElement("", "", "mimetype");
									hd.startElement("", "", "hash", atts);
									hd.characters(artifact.getHash().toCharArray(), 0, artifact.getHash().length());
									hd.endElement("", "", "hash");
									hd.endElement("", "", "artifact");
								} // if
							} // for
							hd.endElement("", "", "artifacts");
						} // if
						
						hd.endElement("", "", "item");
					}
				}
			}

			// Thesauri
			hd.startElement("", "", "thesaurus", atts);

			// Narrower Terms
			if (null != set && set.getNarrowerTerms().length > 0) {
				hd.startElement("", "", "narrower", atts);
				for (int i = 0; i < set.getNarrowerTerms().length; i++) {
					String word = set.getNarrowerTerms()[i];
					hd.startElement("", "", "term", atts);
					hd.characters(word.toCharArray(), 0, word.length());
					hd.endElement("", "", "term");
				}
				hd.endElement("", "", "narrower");
			}

			// Like Terms
			if (null != set && set.getLikeTerms().length > 0) {
				hd.startElement("", "", "like", atts);
				for (int i = 0; i < set.getLikeTerms().length; i++) {
					String word = set.getLikeTerms()[i];
					hd.startElement("", "", "term", atts);
					hd.characters(word.toCharArray(), 0, word.length());
					hd.endElement("", "", "term");
				}
				hd.endElement("", "", "like");
			}

			// Broader Terms
			if (null != set && set.getBroaderTerms().length > 0) {
				hd.startElement("", "", "broader", atts);
				for (int i = 0; i < set.getBroaderTerms().length; i++) {
					String word = set.getBroaderTerms()[i];
					hd.startElement("", "", "term", atts);
					hd.characters(word.toCharArray(), 0, word.length());
					hd.endElement("", "", "term");
				}
				hd.endElement("", "", "broader");
			}

			hd.endElement("", "", "thesaurus");

			// Spelling Suggestion
			hd.startElement("", "", "spellingsuggestion", atts);
			if (null != set) {
				hd.characters(set.getCorrectedTerms().toCharArray(), 0, set.getCorrectedTerms().length());
			}
			hd.endElement("", "", "spellingsuggestion");

			// END </searchresults>
			hd.endElement("", "", "searchresults");
			hd.endDocument();
		} catch (Exception o) {
			o.printStackTrace();
		}

	}

} // class DSMAPIXml

// -- EOF

